package org.goldenport.g3.pipes

import scala.collection.mutable.ArrayBuffer
import org.goldenport.util.Or
import org.goldenport.entity._
import org.goldenport.entity.datasource.NullDataSource
import org.goldenport.entities.csv.CsvEntity
import org.goldenport.entities.xmind.{XMindEntity, XMindNode}
import org.goldenport.entities.opml.OpmlEntity
import org.goldenport.entities.orgmode.OrgmodeEntity
import org.goldenport.value._
import org.goldenport.g3._
import org.goldenport.g3.messages.RecordSet
import org.goldenport.g3.atom.AtomFeed
import org.goldenport.entities.outline.OutlineNode
import org.goldenport.entities.outline.TopicNode

/**
 * @since   Sep. 15, 2010
 * @version Jan. 11, 2011
 * @author  ASAMI, Tomoharu
 */
class G3Convert[A](rule: String, context: G3Context) extends G3Pipe(context) with ActivityActionable {
  val converters = Or[AnyRef, AnyRef](
    List(CsvConverter.create(rule, context),
         OpmlConverter.create(rule, context),
         XMindConverter.create(rule, context),
         OrgmodeConverter.create(rule, context)).flatten: _*)

  def create_Activity(ctx: G3AgentContext): PartialFunction[AnyRef, AnyRef] = converters
}

abstract class G3Converter[-T, +U](val rule: String, val context: G3Context) extends PartialFunction[T, U] {
  protected val input_Handler: PartialFunction[T, U]

  def isDefinedAt(in: T) = {
    input_Handler.isDefinedAt(in)
  }

  def apply(in: T) = input_Handler.apply(in)
}

class CsvConverter(rule: String, context: G3Context) extends G3Converter[AnyRef, AnyRef](rule, context) {
  protected val input_Handler: PartialFunction[AnyRef, AnyRef] = {
    case in: RecordSet => recordset_csv(in)
    case in: AtomFeed => feed_csv(in)
    case in: GTabular[AnyRef] => tabular_csv(in)
    case in: GTree[AnyRef] => tree_csv(in)
  }

  private def to_value(v: AnyRef) = v.toString

  private def recordset_csv(rs: RecordSet) = {
    val csv = new CsvEntity(context.entityContext)
    val columns = new ArrayBuffer[Symbol]

    def get_x(key: Symbol) = {
      if (!columns.contains(key)) columns += key
      columns.indexOf(key)
    }

    var y = 0
    for (r <- rs) {
      for ((k, v) <- r.iterator) {
        csv.put(get_x(k), y, to_value(v))
      }
      y += 1
    }
    csv
  }

  private def feed_csv(feed: AtomFeed) = {
    val csv = new CsvEntity(context.entityContext)
    for (entry <- feed.entries) {
      entry.atomContent.mimeType match {
        case "text/csv" => error("text/csv")
        case "application/json" => error("application/json")
        case _ => error("unknown type")
      }
    }
    csv
  }

  private def tabular_csv(table: GTabular[AnyRef]) = {
    val csv = new CsvEntity(context.entityContext)
    for (y <- 0 until table.height) {
      for (x <- 0 until table.width) {
        for (v <- table.getOption(x, y)) {
          csv.put(x, y, v.toString)
        }
      }
    }
    csv
  }

  private def tree_csv(tree: GTree[AnyRef]) = {
    val csv = new CsvEntity(context.entityContext)
    make_table(tree.root, csv, 0, 0)
    csv
  }

  private def make_table(node: GTreeNode[AnyRef], table: GTabular[String],
                         x: Int, y: Int): Int = {
    var yi = y
    for (n <- node.children) {
      if (n.content != null) {
        table.put(x, yi, n.content.toString)
      }
      yi = make_table(n, table, x + 1, yi)
      yi += 1
    }
    yi
  }
}

object CsvConverter {
  def create(rule: String, context: G3Context): Option[CsvConverter] = {
    if (rule == "csv") Some(new CsvConverter(rule, context))
    else None
  }
}

class OpmlConverter(rule: String, context: G3Context) extends G3Converter[AnyRef, AnyRef](rule, context) {
  protected val input_Handler: PartialFunction[AnyRef, AnyRef] = {
    case in: XMindEntity => xmind_opml(in)
    case in: GTree[AnyRef] => tree_opml(in)
  }

  private def xmind_opml(xmind: XMindEntity) = {
    val opml = new OpmlEntity(NullDataSource, context.entityContext)
    context.createEntity(opml)
    val sheet = xmind.root.children.head
    val topic = sheet.children.head
    opml.name = topic.title
    val thema = new TopicNode(topic.title)
    opml.root.addChild(thema)
    copy_xmind_opml(topic, thema)
    opml
  }

  private def copy_xmind_opml[A, B](src: XMindNode, dest: TopicNode) {
    for (s <- src.children) {
      val d = new TopicNode(s.title)
      dest.addChild(d)
      copy_xmind_opml(s, d)
    }
  }

  private def tree_opml(tree: GTree[AnyRef]) = {
    val opml = new OpmlEntity(NullDataSource, context.entityContext)
    context.createEntity(opml)
    copy_in(tree.root, opml.root)
    opml
  }

  private def copy_in[A, B](src: GTreeNode[A], dest: OutlineNode) {
    for (sc <- src.children) {
      sc.content match {
        case null => {
          val dc = new TopicNode(sc.name)
          dest.addChild(dc)
          copy_in(sc, dc)
        }
        case value => {
          val dc = new TopicNode(sc.name)
          dc.title = value.toString
          dest.addChild(dc)
        }
      }
    }
  }
}

object OpmlConverter {
  def create(rule: String, context: G3Context): Option[OpmlConverter] = {
    if (rule == "opml") Some(new OpmlConverter(rule, context))
    else None
  }
}

class XMindConverter(rule: String, context: G3Context) extends G3Converter[AnyRef, AnyRef](rule, context) {
  protected val input_Handler: PartialFunction[AnyRef, AnyRef] = {
    case in: GTree[AnyRef] => tree_xmind(in)
  }

  private def tree_xmind(tree: GTree[AnyRef]) = {
    val xmind = new XMindEntity(context.entityContext)
    xmind
  }
}

object XMindConverter {
  def create(rule: String, context: G3Context): Option[XMindConverter] = {
    if (rule == "xmind") Some(new XMindConverter(rule, context))
    else None
  }
}

class OrgmodeConverter(rule: String, context: G3Context) extends G3Converter[AnyRef, AnyRef](rule, context) {
  protected val input_Handler: PartialFunction[AnyRef, AnyRef] = {
    case in: GTree[AnyRef] => tree_orgmode(in)
  }

  private def tree_orgmode(tree: GTree[AnyRef]) = {
    val orgmode = new OrgmodeEntity(context.entityContext)
    orgmode
  }
}

object OrgmodeConverter {
  def create(rule: String, context: G3Context): Option[OrgmodeConverter] = {
    if (rule == "orgmode") Some(new OrgmodeConverter(rule, context))
    else None
  }
}
